package com.acdc.wechat.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
 * 微信支付解密工具类
 * 用于解密微信支付通知中的加密数据
 */
public class WechatPayDecryptUtil {
    
    private static final Logger log = LoggerFactory.getLogger(WechatPayDecryptUtil.class);
    
    // AES-GCM算法模式，密钥长度256位
    private static final String ALGORITHM = "AES/GCM/NoPadding";
    private static final int GCM_IV_LENGTH = 12; // GCM模式下IV长度为12字节
    private static final int GCM_TAG_LENGTH = 16; // GCM模式下Tag长度为16字节
    
    /**
     * 解密微信支付通知数据
     * 
     * @param ciphertext 密文
     * @param associatedData 关联数据
     * @param nonce 随机数
     * @param apiV3Key API V3密钥
     * @return 解密后的数据
     * @throws RuntimeException 解密失败时抛出
     */
    public static String decrypt(String ciphertext, String associatedData, String nonce, String apiV3Key) {
        try {
            log.info("开始解密微信支付通知数据");
            log.info("ciphertext长度: {}, associatedData: '{}', nonce: '{}', apiV3Key长度: {}", 
                    ciphertext != null ? ciphertext.length() : 0, 
                    associatedData, nonce, 
                    apiV3Key != null ? apiV3Key.length() : 0);
            
            // 参数验证
            if (ciphertext == null || ciphertext.trim().isEmpty()) {
                throw new RuntimeException("ciphertext不能为空");
            }
            
            if (nonce == null || nonce.trim().isEmpty()) {
                throw new RuntimeException("nonce不能为空");
            }
            
            if (apiV3Key == null || apiV3Key.trim().isEmpty()) {
                throw new RuntimeException("apiV3Key不能为空");
            }
            
            // 处理associatedData，可以为空
            if (associatedData == null) {
                associatedData = "";
            }
            
            // 检查ciphertext是否为Base64编码
            if (!isValidBase64(ciphertext)) {
                log.warn("ciphertext可能不是有效的Base64编码");
            }
            
            // 检查nonce是否为Base64编码
            if (!isValidBase64(nonce)) {
                log.warn("nonce可能不是有效的Base64编码");
            }
            
            // 创建解密器并执行解密
            WechatPayDecryptor decryptor = new WechatPayDecryptor(apiV3Key);
            String result = decryptor.decrypt(associatedData, nonce, ciphertext);
            
            log.info("解密成功，解密后数据长度: {}", result != null ? result.length() : 0);
            return result;
            
        } catch (Exception e) {
            log.error("解密失败", e);
            throw new RuntimeException("解密微信支付通知数据失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 验证字符串是否为有效的Base64编码
     */
    private static boolean isValidBase64(String str) {
        if (str == null || str.trim().isEmpty()) {
            return false;
        }
        
        try {
            Base64.getDecoder().decode(str);
            return true;
        } catch (IllegalArgumentException e) {
            return false;
        }
    }
    
    /**
     * 尝试多种解密方式
     * 如果标准解密失败，尝试其他方式
     */
    public static String decryptWithFallback(String ciphertext, String associatedData, String nonce, String apiV3Key) {
        try {
            // 首先尝试标准解密
            return decrypt(ciphertext, associatedData, nonce, apiV3Key);
        } catch (Exception e) {
            log.warn("标准解密失败，尝试备用解密方式", e);
            
            try {
                // 备用方式1：尝试将nonce进行Base64编码后再使用
                log.info("尝试备用解密方式1：将nonce进行Base64编码");
                WechatPayDecryptor decryptor = new WechatPayDecryptor(apiV3Key);
                
                // 将nonce进行Base64编码
                String encodedNonce = Base64.getEncoder().encodeToString(nonce.getBytes(StandardCharsets.UTF_8));
                log.info("nonce Base64编码后: {}", encodedNonce);
                
                return decryptor.decrypt(associatedData, encodedNonce, ciphertext);
            } catch (Exception e2) {
                log.warn("备用解密方式1失败", e2);
            }
            
            try {
                // 备用方式2：尝试不同的字符编码
                log.info("尝试备用解密方式2：使用ISO-8859-1编码");
                WechatPayDecryptor decryptor = new WechatPayDecryptor(apiV3Key);
                
                // 使用ISO-8859-1编码的associatedData
                String isoAssociatedData = new String(associatedData.getBytes("ISO-8859-1"), StandardCharsets.UTF_8);
                
                return decryptor.decrypt(isoAssociatedData, nonce, ciphertext);
            } catch (Exception e3) {
                log.warn("备用解密方式2失败", e3);
            }
            
            try {
                // 备用方式3：尝试将nonce填充到16字节
                log.info("尝试备用解密方式3：将nonce填充到16字节");
                WechatPayDecryptor decryptor = new WechatPayDecryptor(apiV3Key);
                
                // 将nonce填充到16字节
                String paddedNonce = padNonceTo16Bytes(nonce);
                log.info("nonce填充后: {}", paddedNonce);
                
                return decryptor.decrypt(associatedData, paddedNonce, ciphertext);
            } catch (Exception e4) {
                log.warn("备用解密方式3失败", e4);
            }
            
            try {
                // 备用方式4：尝试将nonce重复到16字节
                log.info("尝试备用解密方式4：将nonce重复到16字节");
                WechatPayDecryptor decryptor = new WechatPayDecryptor(apiV3Key);
                
                // 将nonce重复到16字节
                String repeatedNonce = repeatNonceTo16Bytes(nonce);
                log.info("nonce重复后: {}", repeatedNonce);
                
                return decryptor.decrypt(associatedData, repeatedNonce, ciphertext);
            } catch (Exception e5) {
                log.warn("备用解密方式4失败", e5);
            }
            
            // 所有解密方式都失败，抛出原始异常
            throw new RuntimeException("所有解密方式都失败，原始错误: " + e.getMessage(), e);
        }
    }
    
    /**
     * 将nonce填充到16字节
     */
    private static String padNonceTo16Bytes(String nonce) {
        byte[] nonceBytes = nonce.getBytes(StandardCharsets.UTF_8);
        byte[] paddedNonce = new byte[16];
        
        // 复制原始nonce字节
        System.arraycopy(nonceBytes, 0, paddedNonce, 0, Math.min(nonceBytes.length, 16));
        
        // 如果不足16字节，用0填充
        for (int i = nonceBytes.length; i < 16; i++) {
            paddedNonce[i] = 0;
        }
        
        return new String(paddedNonce, StandardCharsets.UTF_8);
    }
    
    /**
     * 将nonce重复到16字节
     */
    private static String repeatNonceTo16Bytes(String nonce) {
        byte[] nonceBytes = nonce.getBytes(StandardCharsets.UTF_8);
        byte[] repeatedNonce = new byte[16];
        
        // 重复nonce字节直到填满16字节
        for (int i = 0; i < 16; i++) {
            repeatedNonce[i] = nonceBytes[i % nonceBytes.length];
        }
        
        return new String(repeatedNonce, StandardCharsets.UTF_8);
    }
    
    /**
     * 微信支付APIV3解密器内部类
     * 使用AES-GCM算法进行解密
     */
    private static class WechatPayDecryptor {
        
        private final SecretKeySpec secretKeySpec;
        
        /**
         * 构造函数
         * @param apiV3Key 微信支付平台证书的APIv3密钥
         */
        public WechatPayDecryptor(String apiV3Key) {
            // 确保密钥是32字节(256位)，APIv3密钥应为32位字符串
            byte[] keyBytes = apiV3Key.getBytes(StandardCharsets.UTF_8);
            if (keyBytes.length != 32) {
                throw new IllegalArgumentException("APIv3密钥必须是32位字符串");
            }
            this.secretKeySpec = new SecretKeySpec(keyBytes, "AES");
        }
        
        /**
         * 解密微信支付APIV3返回的加密数据
         * @param associatedData 附加数据，对应回调中的associated_data字段
         * @param nonce 随机串，对应回调中的nonce字段
         * @param ciphertext 加密数据，对应回调中的ciphertext字段
         * @return 解密后的明文
         * @throws Exception 解密过程中可能抛出的异常
         */
        public String decrypt(String associatedData, String nonce, String ciphertext) throws Exception {
            // 1. 对加密数据进行Base64解码
            byte[] ciphertextBytes = Base64.getDecoder().decode(ciphertext);
            
            // 2. 准备GCM参数
            // IV由16字节nonce的前12字节组成
            byte[] nonceBytes = nonce.getBytes(StandardCharsets.UTF_8);
            if (nonceBytes.length != 16) {
                throw new IllegalArgumentException("随机串nonce必须是16位字符串，当前长度: " + nonceBytes.length);
            }
            byte[] iv = new byte[GCM_IV_LENGTH];
            System.arraycopy(nonceBytes, 0, iv, 0, GCM_IV_LENGTH);
            
            // 3. 初始化AES-GCM解密器
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, parameterSpec);
            
            // 4. 添加附加数据(associated data)
            if (associatedData != null && !associatedData.isEmpty()) {
                cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));
            }
            
            // 5. 执行解密
            byte[] plaintextBytes = cipher.doFinal(ciphertextBytes);
            
            // 6. 将解密后的字节数组转换为字符串
            return new String(plaintextBytes, StandardCharsets.UTF_8);
        }
    }
}
